封装

Java 的封装特性是面向对象编程的核心原则之一,它指的是将对象的状态(数据)和行为(方法)封装在一个类内部,并通过公开的接口与外部进行交互。封装的主要目的是隐藏对象的内部实现细节,只暴露必要的功能,从而保护数据的完整性和减少系统的复杂性。

扩展知识

基本概念:

  • 数据隐藏:通过将类的字段(成员变量)声明为 privateprotected,避免直接被外部访问。只有通过类提供的公共方法(如 getter 和 setter)才能访问和修改这些字段。
  • 公共接口:通过公共方法(如 getter 和 setter)提供访问对象数据的方式。这样可以对数据进行控制和验证,确保数据的一致性和合法性。
  • 保护数据:封装通过限制对数据的直接访问,减少了对对象状态的不安全修改和潜在的错误。

示例代码

public class Person {
   // 私有字段
   private String name;
   private int age;

   // 公共构造方法
   public Person(String name, int age) {
       this.name = name;
       this.age = age;
   }

   // 公共 getter 方法
   public String getName() {
       return name;
   }

   // 公共 setter 方法
   public void setName(String name) {
       this.name = name;
   }

   // 公共 getter 方法
   public int getAge() {
       return age;
   }

   // 公共 setter 方法
   public void setAge(int age) {
       if (age > 0) {
           this.age = age;
       } else {
           throw new IllegalArgumentException("Age must be positive");
       }
   }
}

扩展知识:

封装的好处:

  • 数据保护:通过隐藏数据和提供受控的访问方法,可以防止外部代码对数据进行不合法的修改。
  • 维护性:封装使得对象的内部实现与外部接口分离,可以更容易地对内部实现进行更改,而不影响外部使用者。
  • 简化接口:提供简洁的公共接口,减少外部代码对类的复杂性理解,从而降低系统的耦合度。
  • 代码复用:通过封装,类可以重用已有的功能而不必重新实现,有助于构建模块化和可维护的代码。

访问控制修饰符:

  • private:只允许类内部访问,无法被外部访问。
  • protected:允许同一包内的类以及子类访问。
  • public:允许任何类访问。
  • 默认(包级别):只允许同一包内的类访问。

补充:封装的更高形态——不可变对象

getter/setter 只是封装的入门。封装更进一步,是干脆不给 setter、不让状态可变——创建之后对象完全不可修改(immutable):

public final class Money {
    private final long cents;      // final 字段,只能在构造时赋值
    private final String currency;

    public Money(long cents, String currency) {
        this.cents = cents;
        this.currency = currency;
    }

    public Money add(Money other) {
        // 不修改自己,返回新对象
        return new Money(this.cents + other.cents, this.currency);
    }
}

不可变对象天然线程安全(没有可改的状态,就不会有并发修改问题),也可以放心地被缓存和共享——String 就是 JDK 里最著名的不可变类。IntegerLocalDate 同理。

Java 16 引入的 record 是不可变数据载体的语法糖:record Point(int x, int y) {} 一行顶过去几十行,字段自动 private final,配套的构造器、访问器、equals/hashCode 全部自动生成。它就是"封装 + 不可变"思想的官方封装版。

补充:什么时候 setter 也是坏味道

一个简单的自查标准:setter 是否让对象在非法状态下存活。比如:

Person p = new Person();
p.setName("张三");
// 此时 age 还没设,p 处于"半成品"状态,却已经能被使用
p.setAge(-5); // 非法状态悄悄通过

更好的做法是用构造器强制"诞生即合法",setter 只保留给确实需要中途变更的字段。封装的本质不是"生成一堆 getter/setter",而是把"保证对象始终处于合法状态"的责任关在类内部


⬅️ OOP 特性 🏠 00-Java ➡️ 引入接口与类之间的隔离